﻿/*	
	stencil - action script 3 isometric game engine.

	http://code.google.com/p/stencilas3/
	
	Copyright (c) 2010 Max Zhuravkov, All Rights Reserved.

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to
	use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
	of the Software, and to permit persons to whom the Software is furnished to do
	so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/
package stencil.core.engine.utils 
{
	import flash.geom.Rectangle;
	import flash.utils.Dictionary;
	import stencil.core.errors.ArgumentIsNullError;
	
	/**
	 * 
	 * @author Max Zhuravkov
	 */
	public class DataGrid{
		
		private var _rect:Rectangle;
		
		private var _cellWidth:int;
		
		private var _cellHeight:int;
		
		private var _rectFunction:Function;
		
		private var _currentRect:Rectangle;
		
		private var _cells:Array;
		
		private var _gridWidth:int;
		
		private var _gridHeight:int;
		
		private var _previousRect:Rectangle;
		
		private var _previousCells:Array;
		
		private var _dictionary:Dictionary = new Dictionary();
		
		//----------------------------------------------------------------
		// Constructor
		//----------------------------------------------------------------
		
		public function DataGrid(rect:Rectangle, cellWidth:uint, cellHeight:uint, rectFunction:Function) {
			this._rect = rect.clone();
			this._cellWidth = cellWidth;
			this._cellHeight = cellHeight;
			this._rectFunction = rectFunction;
			
			this._gridWidth = Math.ceil(rect.width/cellWidth);
			this._gridHeight = Math.ceil(rect.height/cellHeight);
			
			this._cells = new Array(this._gridWidth * this._gridHeight);
			this._dictionary = new Dictionary();
		}
		
		//----------------------------------------------------------------
		// Public properties
		//----------------------------------------------------------------
		
		public function get cellWidth():int {
			return this._cellWidth;
		}
		
		public function get cellHeight():int {
			return this._cellHeight;
		}
		
		public function get rect():Rectangle {
			return this._rect;
		}
		
		//----------------------------------------------------------------
		// Public methods
		//----------------------------------------------------------------
		
		public function contains(o:Object):Boolean {
			if (!o) {
				throw new ArgumentIsNullError("o");
			}
			return this._dictionary[o] ? true : false;
		}
		
		public function put(o:Object):Boolean {
			if (!o) {
				throw new ArgumentIsNullError("o");
			}
			var rect:Rectangle = this._rectFunction.call(null, o);
			
			if (rect == null) {
				throw new Error("rectFunction has returned null. argument = " + o);
			}
			
			var cells:Array = this.getCells(rect);
			
			if (this._dictionary[o]) {
				for each(var oldCell:DataGridCell in this._dictionary[o]) {
					oldCell.remove(o);
				}
			}
			
			if (cells.length > 0) {
				
				for each(var cell:DataGridCell in cells) {
					
					cell.put(o, rect);
				}
				
				this._dictionary[o] = cells;
			}else {
				this._dictionary[o] = null;
				delete this._dictionary[o];
			}
			
			
			
			return cells.length > 0;
		}
		
		public function remove(o:Object):Boolean {
			if (!o) {
				throw new ArgumentIsNullError("o");
			}
			
			if (!this._dictionary[o]) {
				return false;
			}
			
			var rect:Rectangle = this._rectFunction.call(null, o);
			var cells:Array = this.getCells(rect);
			
			for each(var cell:DataGridCell in cells) {
				cell.remove(o);
			}
			
			this._dictionary[o] = cells;
			return true;
		}
		
		public function removeWithin(rect:Rectangle):void {
			if (!rect) {
				throw new ArgumentIsNullError("rect");
			}
			
			if (rect.containsRect(this._rect)) {
				this.clear();
				return;
			}
			
			var cells:Array = this.getCells(rect);
			
			for each(var cell:DataGridCell in cells) {
				
				if (cell.empty) {
					continue;
				}else {
					
					var cx:Number = cell.x * this.cellWidth;
					var cy:Number = cell.y * this.cellHeight;
					var cw:Number = cx + this.cellWidth;
					var ch:Number = cy + this.cellHeight;
					
					
					if (rect.x >= cx && rect.y >= cy && rect.right >= cw && rect.bottom > - ch ) {
						
						for each(var obj:Object in cell.objects) {
							delete this._dictionary[obj];
						}
						
						cell.clear();
						continue;
					}
				}
				
				var length:int = cell.objects.length;
				
				for (var i:int = 0; i < length; i ++ ) {
					var objRect:Rectangle = cell.objectRect(i);
					if (rect.containsRect(objRect)) {
						cell.removeAt(i);
						length --;
						
						var o:Object = cell.objects[i];
						
						delete this._dictionary[o];
						
						if (i > 0) {
							i --;
						}
					}
				}
				
			}
		}
		
		public function clear():void {
			for each(var cell:DataGridCell in this._cells) {
				if (!cell.empty) {
					cell.clear();
				}
			}
			this._dictionary = new Dictionary();
		}
		
		public function getObjects(rect:Rectangle, filter:Function = null):Array {
			if (!rect) {
				throw new ArgumentIsNullError("rect");
			}
			if (!this._rect.intersects(rect)) {
				
				return null;
			}
			
			var cells:Array = this.getCells(rect);
			var objects:Array = new Array();
			
			for each(var cell:DataGridCell in cells) {
				if (cell.empty) {
					continue;
				}
				var oc:int = cell.objects.length;
				
				for (var i:int = 0; i < oc; i ++ ) {
					var objRect:Rectangle = cell.objectRect(i);
					
					if (!rect.intersects(objRect)) {
						continue;
					}

					var o:Object = cell.objects[i];
					
					if (filter != null && !filter(o)) {
						continue;
					}
					
					if(objects.indexOf(o) ==- 1){
						objects.push(o);
					}
				}
			}
			
			return objects;
		}
		
		//----------------------------------------------------------------
		// Protected methods
		//----------------------------------------------------------------
		
		protected function getCells(rect:Rectangle):Array {
			
			var intersection:Rectangle = this._rect.intersection(rect);
		
			var cx:Number = Math.floor((intersection.x - this._rect.x)/this.cellWidth);
			var cy:Number = Math.floor((intersection.y - this._rect.y)/this.cellHeight);
			var cw:Number = Math.ceil(intersection.width/this.cellWidth);
			var ch:Number = Math.ceil(intersection.height/this.cellHeight);
			
			if (this._previousRect && (this._previousRect.x == cx && this._previousRect.y == cy 
										&& this._previousRect.width == cw && this._previousRect.height == ch)){
				return this._previousCells;
			}
			
			
			this._previousCells  = new Array(cw * ch);
			if (!this._previousRect) {
				this._previousRect = new Rectangle();
			}
			this._previousRect.x = cx;
			this._previousRect.y = cy;
			this._previousRect.width = cw;
			this._previousRect.height = ch;
			
			cw += cx;
			ch += cy;
			
			var ic:int = 0;
			
			
			if (cw > this._gridWidth) {
				cw = this._gridWidth;
			}
			
			if (ch > this._gridHeight) {
				ch = this._gridHeight;
			}
			
			for (var i:int = cx; i <= cw; i ++  ) {
				
				for (var j:int = cy; j <= ch; j ++  ) {
					var cellIndex:int = j * this._gridWidth + i;
					var cell:DataGridCell = this._cells[cellIndex];
					if (!cell) {
						cell = new DataGridCell(i, j);
						this._cells[cellIndex] = cell;
					}
					
					this._previousCells[ic] = cell;
					ic ++;
				}
				
			}
			
			return this._previousCells;
		}
	}
}
import flash.geom.Rectangle;
import flash.utils.Dictionary;

internal class DataGridCell {
	
	private var _objects:Array = [];
	
	private var _rects:Array =  [];
	
	private var _x:int;
	
	private var _y:int;
	
	//----------------------------------------------------------------
	// Constructor
	//----------------------------------------------------------------	
	
	public function DataGridCell(x:int, y:int) {
		this._x = x;
		this._y = y;
	}
	
	//----------------------------------------------------------------
	// Public properties
	//----------------------------------------------------------------
	
	public function get empty():Boolean {
		return !this._objects.length;
	}
	
	public function get objects():Array {
		return this._objects;
	}
	
	public function get x():int {
		return this._x;
	}
	
	public function get y():int {
		return this._y;
	}
	
	//----------------------------------------------------------------
	// Public methods
	//----------------------------------------------------------------
	
	public function put(o:Object, rect:Rectangle):void {
		
		if (this._objects.indexOf(o) == -1) {
			var index:int = this._objects.length;
			this._rects.push(rect);
			this._objects.push(o);
		}
	}
	
	public function contains(o:Object):Boolean {
		return this._objects.indexOf(o) >= 0;
	}
	
	public function remove(o:Object):void {
		var index:int = this._objects.indexOf(o);
		if (index >= 0) {
			this._objects.splice(index, 1);
			this._rects.splice(index, 1);
		}
	}
	
	public function removeAt(index:int):void {
		this._objects.splice(index, 1);
		this._rects.splice(index, 1);
	}
	
	public function objectRect(index:int):Rectangle {
		return this._rects[index];
	}
	
	public function clear():void {
		this._objects = [];
		this._rects = [];
	}
	
	public function toString():String {
		return "[x=" + this._x + " y=" + this._y + "]";
	}
}